#include "libutilsconfig.h"
#include "tDateTime.h"

#ifdef HAVE_FTIME
# include <sys/timeb.h>
#endif

tm Time_Gmtime( const time_t* t )
{ 
	
#ifdef _MSC_VER
#if( _MSC_VER >= 1400 )
	tm result;
	gmtime_s( &result, t );
	return result;
#else
	return *gmtime( t );
#endif
#else
	tm result;
	return *gmtime_r( t, &result );
#endif	
}

tm Time_LocalTime( const time_t* t)
{ 
	
#ifdef _MSC_VER
#if( _MSC_VER >= 1400 )
	tm result;
	localtime_s( &result, t );
	return result;
#else
	return *localtime( t );
#endif
#else
	tm result;
	return *localtime_r( t, &result );
#endif
	
	
}


LIB_NS_BEGIN

TDateTime TDateTime::NowUtc()
{
#if defined( HAVE_FTIME )
	timeb tb;
	ftime (&tb);
	return fromUtcTimeT (tb.time, tb.millitm);
#elif defined( _POSIX_SOURCE )
	struct timeval tv;
	gettimeofday (&tv, 0);
	return fromUtcTimeT( tv.tv_sec, tv.tv_usec / 1000 );
#else
	return FromUtcTimeT( ::time (0), 0 );
#endif
}

TDateTime TDateTime::NowLocal()
{
#if defined( HAVE_FTIME )
	timeb tb;
	ftime (&tb);
	return fromLocalTimeT( tb.time, tb.millitm );
#elif defined( _POSIX_SOURCE )
	struct timeval tv;
	gettimeofday (&tv, 0);
	return fromLocalTimeT( tv.tv_sec, tv.tv_usec / 1000 );
#else
	return FromLocalTimeT( ::time (0), 0 );
#endif
}

LIB_NS::TDateTime TDateTime::FromUtcTimeT( time_t t, int millis /*= 0 */ )
{
	struct tm tm = Time_Gmtime( &t );
	return FromTm( tm, millis );
}

TDateTime::TDateTime() : m_date (0), m_time (0)
{
	
}

TDateTime::TDateTime( int date, int time ) : m_date (date), m_time (time)
{
	
}

TDateTime::TDateTime( int year, int month, int day, int hour, int minute, int second, int millis )
{
	m_date = JulianDate( year, month, day );
	m_time = MakeHMS( hour, minute, second, millis );
}

TDateTime::~TDateTime()
{
}

int TDateTime::GetYear() const
{
	int y, m, d;
	GetYMD( y, m, d );
	return y;
}

int TDateTime::GetMonth() const
{
	int y, m, d;
	GetYMD( y, m, d );
	return m;
}

int TDateTime::GetDay() const
{
	int y, m, d;
	GetYMD( y, m, d );
	return d;
}

int TDateTime::GetDate() const
{
	return GetDay();
}

int TDateTime::GetJulianDate() const
{
	return m_date;
}

int TDateTime::GetHour() const
{
	return m_time / MILLIS_PER_HOUR;
}

int TDateTime::GetMinute() const
{
	return (m_time / MILLIS_PER_MIN) % MINUTES_PER_HOUR;
}

int TDateTime::GetSecond() const
{
	return (m_time / MILLIS_PER_SEC) % SECONDS_PER_MIN;
}

int TDateTime::GetMillisecond() const
{
	return m_time % MILLIS_PER_SEC;
}

void TDateTime::GetYMD( int& year, int& month, int& day ) const
{
	GetYMD( m_date, year, month, day );
}

void TDateTime::GetYMD( int jday, int& year, int& month, int& day )
{
	int a = jday + 32044;
	int b = (4 * a + 3) / 146097;
	int c = a - int ((b * 146097) / 4);
	int d = (4 * c + 3) / 1461;
	int e = c - int ((1461 * d) / 4);
	int m = (5 * e + 2) / 153;
	day = e - int ((153 * m + 2) / 5) + 1;
	month = m + 3 - 12 * int (m / 10);
	year = b * 100 + d - 4800 + int (m / 10);
}
void TDateTime::GetHMS( int& hour, int& minute, int& second, int& millis ) const
{
	int ticks = m_time / MILLIS_PER_SEC;
	hour = ticks / SECONDS_PER_HOUR;
	minute = (ticks / SECONDS_PER_MIN) % MINUTES_PER_HOUR;
	second = ticks % SECONDS_PER_MIN;
	millis = m_time % MILLIS_PER_SEC;
}

int TDateTime::GetWeekDay() const
{
	int Y, M, D;
	GetYMD (Y, M, D);
	int m = M >= 3 ? M - 2 : M + 10;
	int Yprime = M >= 3 ? Y : Y - 1;
	int y = Yprime % 100;
	int c = Yprime / 100;
	int wd = (D + int (2.6 * m - 0.2) + y + int (y / 4) + int (c / 4) -
		(2 * c)) % 7;
	return 1 + (wd < 0 ? 7 + wd : wd);
}

time_t TDateTime::GetTimeT() const
{
	return (SECONDS_PER_DAY * (m_date - JULIAN_19700101) +
		m_time / MILLIS_PER_SEC);
}

tm TDateTime::GetTmUtc() const
{
	int year, month, day;
	int hour, minute, second, millis;
	tm result = { 0 };
	
	GetYMD( year, month, day );
	GetHMS( hour, minute, second, millis );
	
	result.tm_year = year - 1900;
	result.tm_mon = month - 1;
	result.tm_mday = day;
	result.tm_hour = hour;
	result.tm_min = minute;
	result.tm_sec = second;
	result.tm_isdst = -1;
	
	return result;
}

void TDateTime::SetYMD( int year, int month, int day )
{
	m_date = JulianDate( year, month, day );
}

void TDateTime::SetHMS( int hour, int minute, int second, int millis )
{
	m_time = MakeHMS( hour, minute, second, millis );
}

void TDateTime::SetMinute( int min )
{
	int hour, old_min, sec, millis;
	GetHMS( hour, old_min, sec, millis );
	SetHMS( hour, min, sec, millis );
}

void TDateTime::SetSecond( int sec )
{
	int hour, min, old_sec, millis;
	GetHMS( hour, min, old_sec, millis );
	SetHMS( hour, min, sec, millis );
}

void TDateTime::SetHour( int hour )
{
	int old_hour, min, sec, millis;
	GetHMS( old_hour, min, sec, millis );
	SetHMS( hour, min, sec, millis );
}

void TDateTime::SetMillisecond( int millis )
{
	int hour, min, sec, old_millis;
	GetHMS( hour, min, sec, old_millis );
	SetHMS( hour, min, sec, millis );
}

void TDateTime::ClearTime()
{
	m_time = 0;
}

void TDateTime::operator+=( int seconds )
{
	int d = seconds / SECONDS_PER_DAY;
	int s = seconds % SECONDS_PER_DAY;
	
	m_date += d;
	m_time += s * MILLIS_PER_SEC;
	
	if( m_time > MILLIS_PER_DAY )
	{
		m_date++;
		m_time %= MILLIS_PER_DAY;
	}
	else if( m_time < 0 )
	{
		m_date--;
		m_time += MILLIS_PER_DAY;
	}
}

int TDateTime::JulianDate( int year, int month, int day )
{
	int a = (14 - month) / 12;
	int y = year + 4800 - a;
	int m = month + 12 * a - 3;
	return (day + int ((153 * m + 2) / 5) + y * 365 +
		int (y / 4) - int (y / 100) + int (y / 400) - 32045);
}

LIB_NS::TDateTime TDateTime::FromTm( const tm& tm, int millis /*= 0 */ )
{
	return TDateTime ( JulianDate(tm.tm_year + 1900, tm.tm_mon + 1,
		tm.tm_mday),
		MakeHMS(tm.tm_hour, tm.tm_min, tm.tm_sec, millis) );
}

LIB_NS::TDateTime TDateTime::FromLocalTimeT( time_t t, int millis /*= 0 */ )
{
	struct tm tm = Time_LocalTime( &t );
	return FromTm( tm, millis );
}

int TDateTime::MakeHMS( int hour, int minute, int second, int millis )
{
	return MILLIS_PER_SEC * (SECONDS_PER_HOUR * hour +
		SECONDS_PER_MIN * minute +
		second) + millis;
}

void TDateTime::Set( const TDateTime& other )
{
	m_date = other.m_date;
	m_time = other.m_time;
}

void TDateTime::ClearDate()
{
	m_date = 0;
}

int TDateTime::GetNextDay( int curdate )
{
	return 0;
}

LIB_NS_END